package cvo.pro4.chess.client.entities;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cvo.pro4.chess.client.entities.pieces.Bishop;
import cvo.pro4.chess.client.entities.pieces.King;
import cvo.pro4.chess.client.entities.pieces.Knight;
import cvo.pro4.chess.client.entities.pieces.Pawn;
import cvo.pro4.chess.client.entities.pieces.Queen;
import cvo.pro4.chess.client.entities.pieces.Rook;
import cvo.pro4.chess.client.exceptions.IllegalCoordinatesException;

public class Board {

	// Sinds tile eigenlijk als wrapper fungeert voor baordcoordinates is dat al zo
	private Map <Tile,Piece> postionOnBoard = new HashMap<Tile, Piece>();
	private List <Piece> deadPieces = new ArrayList<Piece>();
	
	private Army blackArmy, whiteArmy;
	
	/**
	 * Fills the board with Tiles
	 * @throws IllegalCoordinatesException
	 */
	public Board() throws IllegalCoordinatesException {		
		for (int x = 1; x <= 8; x++) {
	        for (int y = 1; y <= 8; y++) {
	        	Tile tile = null; 
	            if ((x + y) % 2 == 0) {	            	
	            	tile = new Tile(BoardCoordinates.create(x, y), Color.BLACK);	            	             
	            } else {
	            	tile = new Tile(BoardCoordinates.create(x, y), Color.WHITE);	            	     
	            }   
	            postionOnBoard.put(tile,null);  
	        }
		}
		
		createArmies();
	}
	
	/**
	 * Set the pieces on the board and create the white and black army
	 * @throws IllegalCoordinatesException 
	 */
	private void createArmies() throws IllegalCoordinatesException{
		this.whiteArmy = new Army(Color.WHITE);
		this.blackArmy = new Army(Color.BLACK);
		
		
		BoardCoordinates coordinates = null;
		coordinates = BoardCoordinates.create(1,1);
		
	
	
		// set all white pawns
		int secondLine = 2;
		for(int i=1 ; i <=8; i++){
			coordinates = BoardCoordinates.create(i, secondLine);
			Piece piece = new Pawn(Color.WHITE);
//			piece.setActive();
			setPieceAtLocation(coordinates, piece);			
			this.whiteArmy.add(piece);
		}
		
		// set all black pawns
		int seventhLine = 7;
		for(int i=1 ; i <=8; i++){
			coordinates = BoardCoordinates.create(i, seventhLine);
			Piece piece = new Pawn(Color.BLACK);
//			piece.setActive();
			setPieceAtLocation(coordinates, piece);			
			this.blackArmy.add(piece);
		}
		
		
		Piece temp = null;
		
		//Set with servants
		int firstline = 1;
		int iWhite = 1;		
		
		temp = new Rook(Color.WHITE);
//		temp.setActive();
		this.whiteArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iWhite++, firstline), temp);
		temp = new Knight(Color.WHITE);
//		temp.setActive();
		this.whiteArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iWhite++, firstline), temp);
		temp = new Bishop(Color.WHITE);
//		temp.setActive();
		this.whiteArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iWhite++, firstline), temp);
		temp = new King(Color.WHITE);
//		temp.setActive();
		this.whiteArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iWhite++, firstline), temp);
		temp = new Queen(Color.WHITE);
//		temp.setActive();
		this.whiteArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iWhite++, firstline), temp);	
		temp = new Bishop(Color.WHITE);
//		temp.setActive();
		this.whiteArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iWhite++, firstline), temp);
		temp = new Knight(Color.WHITE);
//		temp.setActive();
		this.whiteArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iWhite++, firstline), temp);
		temp = new Rook(Color.WHITE);
//		temp.setActive();
		this.whiteArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iWhite++, firstline), temp);
		
		//Set black servants
		int lastline = 8;
		int iBlack = 1;		
		
		temp = new Rook(Color.BLACK);
//		temp.setActive();
		this.blackArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iBlack++, lastline), temp);
		temp = new Knight(Color.BLACK);
//		temp.setActive();
		this.blackArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iBlack++, lastline), temp);
		temp = new Bishop(Color.BLACK);
//		temp.setActive();
		this.blackArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iBlack++, lastline), temp);
		temp = new Queen(Color.BLACK);
//		temp.setActive();
		this.blackArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iBlack++, lastline), temp);	
		temp = new King(Color.BLACK);
//		temp.setActive();
		this.blackArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iBlack++, lastline), temp);		
		temp = new Bishop(Color.BLACK);
//		temp.setActive();
		this.blackArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iBlack++, lastline), temp);
		temp = new Knight(Color.BLACK);
//		temp.setActive();
		this.blackArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iBlack++, lastline), temp);
		temp = new Rook(Color.BLACK);
//		temp.setActive();
		this.blackArmy.add(temp);
		setPieceAtLocation(BoardCoordinates.create(iBlack++, lastline), temp);
		
	
	}
	
	/**
	 * Get the number of Tiles
	 * @return an Integer with the number of Tiles
	 */
	public Integer getNumberOfTiles(){
		return postionOnBoard.size();
	}


	/**
	 * Get a tile from the board at the desired coordinates
	 * 
	 * @param coordinates
	 * @return The tile in question or null if no tile found or bad coordinates
	 * @throws IllegalCoordinatesException 
	 */
	public Tile getTileAt(BoardCoordinates coordinates) throws IllegalCoordinatesException {
		if(coordinates == null){
			return null;
		}
		
		for(Map.Entry<Tile, Piece> entry : postionOnBoard.entrySet()){
				if (entry.getKey().getCoordinates().equals(coordinates)) {
					return entry.getKey();
				}
		}
		
		return null;
	}
	
	/**
	 * Get the tile from piece
	 * @param piece
	 * @return
	 */
	public Tile getTile(Piece piece){
		if(piece == null){
			return null;
		}
		for(Map.Entry<Tile, Piece> entry : postionOnBoard.entrySet()){
			if (entry.getValue() != null && entry.getValue().equals(piece)) {
				return entry.getKey();
			}
		}
		return null;
	}
	
	
	/**
	 * Gets the location of the given Piece
	 * @param piece
	 * @return BoardCoordinates of the Piece
	 */
	public BoardCoordinates getLocationOfPiece(Piece piece){
		if(piece != null && piece.isActive()){
			BoardCoordinates coord = this.getTile(piece).getCoordinates();		
			return coord;
		}
		return null;
	}

	/**
	 * Return the piece if it is present at given coordinates
	 * 
	 * @param coordinates
	 * @return The piece in question or null if no tile found, no piece found or
	 *         bad coordinates
	 * @throws IllegalCoordinatesException 
	 */
	public Piece getPieceAtLocation(BoardCoordinates coordinates) throws IllegalCoordinatesException {
		if(coordinates == null){
			return null;
		}
		Tile tile = getTileAt(coordinates);
		Piece pieceFound = this.postionOnBoard.get(tile);
		return pieceFound;
	}
	
	/**
	 * Sets a piece at the given location and sets the piece to active (piece may on not be active)
	 * Two scenario's
	 * a) Piece can be on postMortuum and be inactive and set somewhere thus coming back alive
	 * b) Piece may be present on the Board and be place somewhere else
	 * @param coordinates
	 * @param piece
	 * @throws IllegalCoordinatesException
	 */
	public void setPieceAtLocation(BoardCoordinates coordinates, Piece piece)throws IllegalCoordinatesException{
		//See if coordinates and piece are not null and it is not occupied
		if(coordinates == null || piece == null || getPieceAtLocation(coordinates) != null){
			return;
		}		
		
		Tile tile = getTileAt(coordinates);
		
		if(piece.isActive() == null && !this.postionOnBoard.containsValue(piece) && !this.deadPieces.contains(piece)){
			//scenario one, piece is neither dead or alive
			this.postionOnBoard.put(tile, piece);
			piece.setActive();				
		} else if(piece.isActive()== false && this.deadPieces.contains(piece)){
			//scenario two, piece is in postMortuum
			this.deadPieces.remove(piece);
			this.postionOnBoard.put(tile, piece);
			piece.setActive();			
		} else if(piece.isActive() && this.postionOnBoard.containsValue(piece)){
			//scenario three, piece is on Board and is placed somewhere else
			//get original tile where the piece is 
			Tile originalTile = getTile(piece);
			this.postionOnBoard.put(originalTile, null);
			//put it on new tile
			this.postionOnBoard.put(tile, piece);			
		} else {
			//this may never be reached
			throw new IllegalStateException("Piece must be either on Board and be active or must be inactive and in postMortuum!");
		}
	}
	
	/**
	 * Remove the piece given
	 * @param piece
	 */
	public void removePiece(Piece piece){
		if(piece != null && piece.isActive()){
			//If the piece is active but is not present on the board something is broken
			if(!postionOnBoard.containsValue(piece)){
				throw new IllegalStateException("Piece was active but not on board!");
			}
			Tile tileFromPiece = getTile(piece);
			postionOnBoard.put(tileFromPiece, null);
			piece.setInactive();
			deadPieces.add(piece);
		}				
	}
	
	/**
	 * Remove the piece at the given BoardCoordinates
	 * @param coordinates
	 * @throws IllegalCoordinatesException 
	 */
	public void removePiece(BoardCoordinates coordinates) throws IllegalCoordinatesException{
		if(coordinates == null){
			return;
		}
		
		Piece piece = getPieceAtLocation(coordinates);
		//if there is a piece
		if(piece != null){
			removePiece(coordinates);
		}		
	}
	
	
	/**
	 * Gets the with army
	 * @return Army of white pieces
	 */
	public Army getWhiteArmy(){
		return this.whiteArmy;
	}
	
	/**
	 * Gets the black army
	 * @return Army of black pieces
	 */
	public Army getBlackArmy(){
		return this.blackArmy;
	}
	
	/**
	 * Gives you the dead pieces
	 * @return List with dead Pieces
	 */
	public List<Piece> getDeadPieces(){
		return this.deadPieces;
	}
	
	/**
	 * Get either all dead white pieces or all dead black pieces
	 * @param color
	 * @return
	 */
	public List<Piece> getDeadPieces(Color color){
		if(color == null){
			return null;
		}
		
		if(Color.WHITE.equals(color)){
			List <Piece> deadWhitePieces = new ArrayList<Piece>();
			for (Piece piece : deadPieces) {
				if(whiteArmy.getPieces().contains(piece)){
					deadWhitePieces.add(piece);
				}
			}
			return deadWhitePieces;
		} else {
			List <Piece> deadBlackPieces = new ArrayList<Piece>();
			for (Piece piece : deadPieces){
				if(blackArmy.getPieces().contains(piece)){
					deadBlackPieces.add(piece);
				}
			}
			return deadBlackPieces;
		}
			
	}
	
	
	
	
	
	
	public Map <Tile, Piece> getMapping(){
		return this.postionOnBoard;
	}

}